﻿using System;
using System.Reflection;
using LucyGrid;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Threading;
using System.Dynamic;

namespace LucyDemo
{
	/// <summary>
	/// BASED ON: Pattrens of parallel programming C#
	/// http://download.microsoft.com/download/3/4/D/34D13993-2132-4E04-AE48-53D3150057BD/Patterns_of_Parallel_Programming_CSharp.pdf&ei=O0SFVafYDMitU9DegrgE&usg=AFQjCNGhk_BZL8-5n8DaS_kMTaWRU9Y1Zw&sig2=M-so79PHZayYWMxzaH4UfQ
	/// </summary>
	class Program
	{
		static AbstractDemo[] Examples;

		static Program() {
			Examples = Assembly.GetExecutingAssembly()
					.GetTypes().Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(AbstractDemo)))
					.Select(t => (AbstractDemo)Activator.CreateInstance(t))
					.ToArray();
		}

		GridComm GridBus;
		Dictionary<string, JObject> Grids = new Dictionary<string, JObject>();
		public Program() {
			GridBus = new GridComm();
			GridBus.OnEventReceived += (evname, ev) => {
				if (evname == GridCommEvents.grid) {
					var key = ev["Id"].ToString();
					if(!Grids.ContainsKey(key))
						Grids.Add(key, ev);
					else
						Grids[key] = ev;
				}
			};
 			GridBus.StartListener();
		}

		

		static void Main(string[] args)
		{
			Console.Clear();
			GridUtils.EnableLog = (int)GridUtils.LogType.ALL;
			new Program().Menu();
		}

		/// <summary>
		/// Switches local processing
		/// </summary>
		bool LocalProcessing = true;

		void Menu() {
			ConsoleKeyInfo key;
			while ((key = Home()).Key != ConsoleKey.Escape)
			{
				switch (key.KeyChar) {
				case 'a':
					LocalProcessing = true;
						break;
				case 'b':
					LocalProcessing = false;
					break;
					default:
					RunExample (int.Parse (key.KeyChar + ""));
					break;
				}
			};
		}
		
		private void RunExample(int p)
		{
			var example = Examples[p];
			Output("Running example " + p);
			Output(example.Info());
			SetParameters(example);
			var sw = System.Diagnostics.Stopwatch.StartNew();
			Output("Running example...");
			var t = UpdateGridInfo();
			Output(example.Run(LocalProcessing));
			t.Stop();
			Output(string.Format("Done in {0} s", sw.ElapsedMilliseconds / 1000));
			Output("Press any key to continue...");
			Console.ReadKey();
		}

		void SetParameters(AbstractDemo demo) {
			demo.Parameters =
				demo.Parameters
				.ToDictionary(p => p.Key, p =>
				{
					Console.Write("\r\nEnter value for [{0}]{1} (def {2}): ", p.Value[0], p.Key, p.Value[1]);
					var value = Console.ReadLine();
					if (string.IsNullOrEmpty(value))
						value = p.Value[1];
					return new string[]{ p.Value[0], value };
				});
		}

		ConsoleKeyInfo Home() {
			Console.Clear();
			Output(
string.Format(@"LucyGrid DEMO

{0}

Local processing is: {1}
a. Enable local processing
b. Diable local processing (only nodes will process jobs)

Esc. Exit

Choose a demo:", 
			   string.Join("\r\n",  Examples.Select((e, i) => string.Format("{0}. {1}: {2}", i, e.GetType().Name, e.Info())).ToArray()),
			   LocalProcessing ? "Enabled": "Disabled"
			));
			return Console.ReadKey();
		}

		private void Output(string p)
		{
			Console.WriteLine(p);
		}

		dynamic UpdateGridInfo() {
			return CreateRunner(() =>{
				// System.Diagnostics.Debug.WriteLine("Runner called");
			}, 300);
		}
		dynamic CreateRunner(Action callback, int sleepTime){
			dynamic d = new ExpandoObject();
			d.Running = true;
			d.Task = Task.Run(() => {
					Thread.CurrentThread.Name = string.Format("RUNNER-{0}", Thread.CurrentThread.ManagedThreadId);
					while(d.Running){
						callback();
						Thread.Sleep(sleepTime);
					}
				});
			d.Stop = new Action(() =>
			{
				d.Running = false;
				d.Task.Wait();
			});
			return d;
		}
	}
}
